﻿
#include "string_util.h"

#include <iostream>
#include <random>

#if _WIN32 || WIN32
#include <Windows.h>
#endif

namespace cppc {

static const char s_hex[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
static const std::vector<std::string> s_unitList = {"B", "KB", "MB", "GB", "TB", "PB", "EB"};

std::string formatByteSize(uint64 byteSize, int decimalNum) {
    std::ostringstream out;
    out.setf(std::ios::fixed);
    uint unitStep = 0;
    double unitValue = (double)byteSize;
    while (unitValue >= double(FACTOR_KB_TO_BYTE) && unitStep < s_unitList.size()) {
        unitValue = unitValue / FACTOR_KB_TO_BYTE;
        unitStep++;
    }
    out << std::setprecision(decimalNum) << unitValue;
    out << s_unitList[unitStep];
    return out.str();
}

uint64 parseByteSize(const std::string &byteStr) {
    std::string valueStr = byteStr;
    toUpperStr(valueStr);
    trim(valueStr);
    double sizeValue = std::atof(valueStr.c_str());
    if (sizeValue > 0) {
        std::string::size_type const len = valueStr.length();

        if (len > 2) {
            std::string unitStr = valueStr.substr(valueStr.size() - 2, 2);
            size_t unitStep = 0;
            for (size_t i = s_unitList.size() - 1; i > 0; i--) {
                if ((unitStr.compare(s_unitList[i]) == 0)) {
                    unitStep = i;
                    break;
                }
            }
            while (unitStep > 0) {
                sizeValue *= FACTOR_KB_TO_BYTE;
                unitStep--;
            }
        }
        return uint64(sizeValue);
    } else {
        return 0;
    }
}

bool isDigitStr(const std::string &str) {
    for (uint i = 0; i < str.size(); i++) {
        const char &tmp = str.at(i);
        if (isDigitChar(tmp)) {
            continue;
        } else {
            return false;
        }
    }
    return true;
}

std::string &toLowerStr(std::string &str) {
    uint len = (uint)str.size();
    for (uint i = 0; i < len; i++) {
        if (isUpperChar(str[i])) {
            str[i] += 32; // +32转换为小写
        }
    }
    return str;
}

std::string toLowerStr(std::string &&str) {
    std::string t_str = str;
    toLowerStr(t_str);
    return std::move(t_str);
}
std::string &toUpperStr(std::string &str) {
    uint len = (uint)str.size();
    for (uint i = 0; i < len; i++) {
        if (isLowerChar(str[i])) {
            str[i] -= 32; // -32转换为大写
        }
    }
    return str;
}

std::string toUpperStr(std::string &&str) {
    std::string t_str = str;
    toUpperStr(t_str);
    return std::move(t_str);
}
char toHexChar(uint8 x) {
    x = x % 16;
    return s_hex[x];
}

uint8 toUInt(char hex) {
    if (isDigitChar(hex)) {
        return uint8(hex - '0');
    } else if (isUpperChar(hex)) {
        return uint8(hex - 'A' + 10);
    } else if (isLowerChar(hex)) {
        return uint8(hex - 'a' + 10);
    }
    return 0;
}

std::string toHexString(const char *in, uint length, char delim) {
    std::string str;
    for (size_t i = 0; i < length; ++i) {
        uint8 t = (uint8)in[i];
        uint8 a = t / 16;
        uint8 b = t % 16;
        str.append(1, toHexChar(a));
        str.append(1, toHexChar(b));
        if ((delim != '\0') && (i != length - 1)) {
            str.append(1, delim);
        }
    }
    return str;
}

void trim(std::string &s, const std::string chars) {
    std::string map(0xFF, '\0');
    for (auto &ch : chars) {
        map[(unsigned char &)ch] = '\1';
    }
    while (s.size() && map.at((unsigned char &)s.back())) {
        s.pop_back();
    }
    while (s.size() && map.at((unsigned char &)s.front())) {
        s.erase(0, 1);
    }
}

void replace(std::string &str, const std::string &oldStr, const std::string &newStr, std::string::size_type startPos) {
    if (oldStr.empty() || oldStr == newStr) {
        return;
    }
    auto pos = str.find(oldStr, startPos);
    if (pos == std::string::npos) {
        return;
    }
    str.replace(pos, oldStr.size(), newStr);
    replace(str, oldStr, newStr, pos + newStr.length());
}

std::string join(std::set<std::string> &strSet, const std::string &pattern) {
    std::string valueStr;
    std::set<std::string>::iterator it; // 定义前向迭代器
    // 中序遍历集合中的所有元素
    uint readNum = 0;
    for (it = strSet.begin(); it != strSet.end(); it++) {
        readNum++;
        valueStr.append(*it);
        if (readNum != strSet.size()) {
            valueStr.append(pattern);
        }
    }
    return valueStr;
}

std::vector<std::string> split(const std::string &str, const std::string &delim) {
    std::vector<std::string> ret;
    size_t last = 0;
    auto index = str.find(delim, last);
    while (index != std::string::npos) {
        if (index - last > 0) {
            ret.push_back(str.substr(last, index - last));
        }
        last = index + delim.size();
        index = str.find(delim, last);
    }
    if (!str.size() || str.size() - last > 0) {
        ret.push_back(str.substr(last));
    }
    return ret;
}

std::vector<std::string> splitToken(const std::string &tokenStr, const std::string &delimiters) {
    std::vector<std::string> tokens;
    std::string str;
    for (auto &ch : tokenStr) {
        if (delimiters.find_first_of(ch) != std::string::npos) {
            if (str.size()) {
                tokens.push_back(str), str = "";
            }
            std::string chStr = std::string() + ch;
            // 正常情况下，=号不可能是第一个字符，分隔符也不应连续出现
            if (!tokens.empty() && (tokens[tokens.size() - 1] != chStr)) {
                tokens.push_back(chStr);
            }
        } else {
            str += ch;
        }
    }
    if (!str.empty()) {
        tokens.push_back(str);
    }
    return tokens;
}

bool startWith(const std::string &str, const std::string &substr) {
    return str.find(substr) == 0;
}

bool endWith(const std::string &str, const std::string &substr) {
    auto pos = str.rfind(substr);
    return pos != std::string::npos && pos == str.size() - substr.size();
}
std::string limitStringSize(const std::string &str, uint maxSize) {
    std::string t_str = str;
    if (t_str.size() + 1 > maxSize) {
        auto erased = t_str.size() + 1 - maxSize + 3;
        t_str.replace(5, erased, "...");
    }
    return str;
}
std::vector<char> hexStringToChars(const std::string &strSrc, const std::string &delim) {
    std::vector<char> out;
    std::string str = strSrc;
    if (delim.size() > 0) {
        replace(str, delim, "");
    }

    for (uint i = 0; i < str.length(); i += 2) {
        uint8 a = toUInt((str.at(i)));
        uint8 b = toUInt((str.at(i + 1)));
        out.push_back((char)(a * 16 + b));
    }
    return out;
}

std::string charsToString(const std::vector<char> &chars) {
    std::string str(chars.size(), '\0');
    for (uint i = 0; i < chars.size(); i++) {
        str[i] = (chars[i]);
    }
    return str;
}

uint hexStringToUInt(const std::string &strSrc, const std::string &delim) {
    std::string str = strSrc;
    replace(str, delim, "");
    return strtoul(str.c_str(), 0, 16);
}

int hexStringToInt(const std::string &strSrc, const std::string &delim) {
    std::string str = strSrc;
    replace(str, delim, "");
    return strtol(str.c_str(), 0, 16);
}

std::string toHexString(uint value, int byteLength, bool prefix) {
    std::string valueStr;
    char c[9]{'\0'};

    if (byteLength == 1) {
        sprintf(c, "%02X", value);
    } else if (byteLength == 2) {
        sprintf(c, "%04X", value);
    } else if (byteLength == 3) {
        sprintf(c, "%06X", value);
    } else {
        sprintf(c, "%08X", value);
    }
    if (prefix) {
        valueStr.append("0X");
    }
    valueStr.append(c);
    return valueStr;
}

std::string toHexString(int value, int byteLength, bool prefix) {
    std::string valueStr;
    char c[9]{'\0'};

    if (byteLength == 1) {
        sprintf(c, "%02X", value);
    } else if (byteLength == 2) {
        sprintf(c, "%04X", value);
    } else if (byteLength == 3) {
        sprintf(c, "%06X", value);
    } else {
        sprintf(c, "%08X", value);
    }
    if (prefix) {
        valueStr.append("0X");
    }
    valueStr.append(c);
    return valueStr;
}

std::string toMinHexString(uint value, bool prefix) {
    if (value > 0XFFFFFF) {
        return toHexString(value, 4, prefix);
    } else if (value > 0XFFFF) {
        return toHexString(value, 3, prefix);
    } else if (value > 0XFF) {
        return toHexString(value, 2, prefix);
    } else {
        return toHexString(value, 1, prefix);
    }
}

std::string toMinHexString(int value, bool prefix) {
    if (value > 0XFFFFFF) {
        return toHexString(value, 4, prefix);
    } else if (value > 0XFFFF) {
        return toHexString(value, 3, prefix);
    } else if (value > 0XFF) {
        return toHexString(value, 2, prefix);
    } else {
        return toHexString(value, 1, prefix);
    }
}

#if _WIN32 || WIN32
std::string unicodeToAscii(const std::wstring &str) {
    std::string strDes;
    if (str.empty()) {
        return strDes;
    }
    int nLen = ::WideCharToMultiByte(CP_ACP, 0, str.c_str(), (uint)str.size(), NULL, 0, NULL, NULL);
    if (nLen <= 0) {
        return strDes;
    }
    char *pBuffer = new char[nLen + 1];
    memset(pBuffer, 0, nLen + 1);
    ::WideCharToMultiByte(CP_ACP, 0, str.c_str(), (uint)str.size(), pBuffer, nLen, NULL, NULL);
    pBuffer[nLen] = '\0';
    strDes.append(pBuffer);
    delete[] pBuffer;

    return strDes;
}

std::wstring asciiToUnicode(const std::string &str) {
    std::wstring strDes;
    if (str.empty()) {
        return strDes;
    }
    uint nLen = ::MultiByteToWideChar(CP_ACP, 0, str.c_str(), (uint)str.size(), NULL, 0);
    if (0 <= nLen) {
        return strDes;
    }
    wchar_t *pBuffer = new wchar_t[nLen + 1];
    memset(pBuffer, 0, nLen + 1);
    ::MultiByteToWideChar(CP_ACP, 0, str.c_str(), (uint)str.size(), pBuffer, nLen);
    pBuffer[nLen] = '\0';
    strDes.append(pBuffer);
    delete[] pBuffer;

    return strDes;
}

std::wstring stringToWstring(const std::string &str) {
    LPCSTR pszSrc = str.c_str();
    int nLen = MultiByteToWideChar(CP_ACP, 0, pszSrc, -1, NULL, 0);
    if (nLen <= 0) {
        return std::wstring(L"");
    }

    wchar_t *pwszDst = new wchar_t[nLen];
    if (!pwszDst) {
        return std::wstring(L"");
    }

    MultiByteToWideChar(CP_ACP, 0, pszSrc, -1, pwszDst, nLen);
    std::wstring wstr(pwszDst);
    delete[] pwszDst;
    pwszDst = NULL;

    return wstr;
}

std::string wstringToString(const std::wstring &wstr) {
    LPCWSTR pwszSrc = wstr.c_str();
    int nLen = WideCharToMultiByte(CP_ACP, 0, pwszSrc, -1, NULL, 0, NULL, NULL);
    if (nLen <= 0) {
        return std::string("");
    }

    char *pszDst = new char[nLen];
    if (!pszDst) {
        return std::string("");
    }

    WideCharToMultiByte(CP_ACP, 0, pwszSrc, -1, pszDst, nLen, NULL, NULL);
    std::string str(pszDst);
    delete[] pszDst;
    pszDst = NULL;

    return str;
}

std::string unicodeToUtf8(const std::wstring &wstrUnicode) {
    std::string strRet;
    if (wstrUnicode.empty()) {
        return strRet;
    }
    int nLen = WideCharToMultiByte(CP_UTF8, 0, wstrUnicode.c_str(), -1, NULL, 0, NULL, NULL);
    if (nLen < 0) {
        return strRet;
    }
    char *pBuffer = new char[nLen + 1];
    pBuffer[nLen] = '\0';
    nLen = WideCharToMultiByte(CP_UTF8, 0, wstrUnicode.c_str(), -1, pBuffer, nLen, NULL, NULL);
    strRet.append(pBuffer);
    delete[] pBuffer;
    return strRet;
}

std::wstring utf8ToUnicode(const std::string &str) // utf8转Unicode
{
    std::wstring wStr;
    int u16Len = ::MultiByteToWideChar(CP_UTF8, NULL, str.c_str(), (int)str.size(), NULL, 0);
    if (u16Len < 0) {
        return wStr;
    }
    wchar_t *wstrBuf = new wchar_t[u16Len + 1];
    ::MultiByteToWideChar(CP_UTF8, NULL, str.c_str(), (int)str.size(), wstrBuf, u16Len);
    wstrBuf[u16Len] = L'\0';
    wStr.assign(wstrBuf, u16Len);
    delete[] wstrBuf;
    return wStr;
}
#endif

std::string makeRandStr(int size, bool printable) {
    std::string ret;
    ret.resize(size);
    static constexpr char CCH[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    std::mt19937 rng(std::random_device{}());
    for (int i = 0; i < size; ++i) {
        if (printable) {
            uint32_t x = rng() % (sizeof(CCH) - 1);
            ret[i] = CCH[x];
        } else {
            ret[i] = rng() % 0xFF;
        }
    }
    return ret;
}
} // namespace cppc